﻿namespace JoinBox.Basal;

using System;
using System.Collections.Generic;
using System.Threading;

/// <summary>
/// 线程上下文
/// </summary>
public class SyncContext : SynchronizationContext
{
    #region 成员
    struct TaskStruct
    {
        public bool IsPost;
        public Action Task;
    }

    readonly List<TaskStruct> _tasks = new();

    /// <summary>
    /// 当前上下文的缓存<br/>
    /// 它虽然是父类的Current(<see cref="SynchronizationContext.Current"/>)赋值的,但是不完全相等<br/>
    /// 因为父类的没有保留上次的能力<br/>
    /// 缓存的意义:<br/>
    /// 当使用cad的新建图纸对话框(其他对话框也是),此时 <see cref="SynchronizationContext.Current"/>==null,<br/>
    /// 若没有缓存的时候,会直接卡死<br/>
    /// </summary>
    SynchronizationContext? _contextCache;

    /// <summary>
    /// 存在上下文缓存
    /// </summary>
    public bool ExistContextCache => _contextCache != null;

    // 线程锁标记
    readonly object _objLock = new();
    // 线程的开关_第一种
    readonly ManualResetEvent _OnOff = new(true);// 一直放行,除非遇到信号
    // 线程的开关_第二种: https://article.itxueyuan.com/prodBZ
    // readonly AutoResetEvent OnOff = new(true);参数true就是放行状态,循环一次它就变阻塞了
    #endregion

    #region 方法
    /// <summary>
    /// 阻塞主线程<br/>
    /// 写在子类化的消息循环或者窗体空闲事件上会不断的循环它<br/>
    /// 然后才能使用Send和Post<br/>
    /// 但是Send会无法使用getpoint等交互函数,所以全部用Post
    /// </summary>
    protected void WaitOne()
    {
        // 一旦不为null,一直不为null,否则无法获取前一个上下文
        if (Current is null)
            return;
        _contextCache = Current;
        _OnOff.WaitOne(); // 接收到信号就会阻塞线程
    }

    /// <summary>
    /// 子线程发送同步上下文处理<br/>
    /// </summary>
    /// <param name="task">任务</param>
    public void Send(Action task)
    {
        SendOrPost(task, Send);
    }
    public override void Send(SendOrPostCallback d, object? state = null)
    {
        _contextCache!.Send(d, state);
    }

    /// <summary>
    /// 子线程发送异步上下文处理<br/>
    /// </summary>
    /// <param name="task">任务</param>
    public void Post(Action task)
    {
        SendOrPost(task, Post);
    }
    public override void Post(SendOrPostCallback d, object? state = null)
    {
        _contextCache!.Post(d, state);
    }

    /// <summary>
    /// 子线程调用发送同步/异步上下文<br/>
    /// <see cref="_OnOff.Reset"/>会发送信号给<see cref="WaitOne()"/>阻塞主线程
    /// </summary>
    /// <param name="task">任务</param>
    /// <param name="sendOrPost">同步/异步</param>
    void SendOrPost(Action task, Action<SendOrPostCallback, object?> sendOrPost)
    {
        if (_contextCache is null)
        {
            _tasks.Add(new() { IsPost = true, Task = task });
            return;
        }

        lock (_objLock)
        {
            // 设置标记阻塞主线程
            if (!_OnOff.Reset())
                return;
            try
            {
                // 储存当次
                _tasks.Add(new() { IsPost = true, Task = task });
                for (int i = 0; i < _tasks.Count; i++)
                {
                    var item = _tasks[i];/*不要简化这句,因为是多线程,它会放到新变量上,否则post无法获取索引*/
                    sendOrPost.Invoke(d => item.Task.Invoke(), null);
                }
                _tasks.Clear();
            }
            finally
            {
                // 设置标记放行主线程
                _OnOff.Set();
            }
        }
    }
    #endregion
}